package blazingbubble;

import blazingbubble.game.ILobbyGameEventListener;
import moltencore.game.Settings;

import org.apache.log4j.Logger;

import blazingbubble.config.Config;
import blazingbubble.config.ConfigReader;
import blazingbubble.config.exception.ConfigReaderException;
import blazingbubble.exception.StartupException;
import blazingbubble.config.exception.ConfigValidationException;
import blazingbubble.lobby.Lobby;
import blazingbubble.lobby.command.net.NetLobbyCommandFactory;
import blazingbubble.graphics.GameWindow;
import blazingbubble.network.game.NetGame;
import blazingbubble.network.game.GameStrategyFactory;
import blazingbubble.network.game.strategy.IStartGameListener;
import blazingbubble.network.lobby.LobbyStrategyFactory;
import blazingbubble.game.Game;
import blazingbubble.game.command.GameCommandFactory;
import blazingbubble.game.command.net.NetGameCommandFactory;

import spark.utility.Clock;
import spark.utility.FrameBreak;

import java.io.File;
import java.net.InetSocketAddress;

public class BlazingBubble implements ILobbyGameEventListener, IStartGameListener {
	private static Logger logger = Logger.getLogger(BlazingBubble.class);

	private static final String USAGE =
			"<configFile> <resourcesPath>\n\n" +
			"-configFile: path to the file specifying the server config (usually server.ini).\n" +
			"-resourcesPath: path to the resources folder";
	private static final String NO_RESOURCES =
			"Resources folder does not exist";

	private static final int ARGS_INDEX_CONFIG = 0;
	private static final int ARGS_INDEX_RESOURCES = 1;
	private static final int ARGS_COUNT = 2;

	private GameWindow gameWindow;
	private Game game = new Game();

	private Lobby lobby = new Lobby();

	private NetGame netGame = new NetGame();
	private GameCommandFactory gameCommandFactory;

	private Config config;

	private BlazingBubble() {
		super();
	}

	public void execute() {
		FrameBreak frameBreak = new FrameBreak(config.getFrameBreak());
		Clock clock = new Clock();
		clock.reset();

		double x = 0;

		gameWindow.setVisible(true);
		while(gameWindow.isDisplayable()) {
			clock.update();
			frameBreak.breakFrame(clock.getElapsedMilliseconds());
			gameWindow.update();
			if(game.isActive()) {
				game.update(clock.getElapsedSeconds());
			}
		}
	}

	public void startup(Config config) throws StartupException {
		this.config = config;

		LobbyStrategyFactory lobbyStrategyFactory = new LobbyStrategyFactory();
		lobby.startup(config, lobbyStrategyFactory);
		NetLobbyCommandFactory lobbyNetCommandFactory = new NetLobbyCommandFactory();
		lobbyNetCommandFactory.startup(lobby.getSender(), lobby.getServerConnection(), lobby.getQueryPort());

		lobby.setPreLoginResponseListener(lobbyNetCommandFactory.getLogin());

		gameCommandFactory = new NetGameCommandFactory();

		gameWindow = new GameWindow(gameCommandFactory, lobbyNetCommandFactory, game, this, config.getFrameBreak());
		lobbyStrategyFactory.setGameListUpdateListener(gameWindow.getGameListUpdateListener());
		lobbyStrategyFactory.setGameCreationListener(this);
		lobbyStrategyFactory.setLoginResponseResultListener(gameWindow);
		lobbyStrategyFactory.setJoinGameCommand(gameCommandFactory.getJoinGame());

		gameWindow.startup(config);
	}

	private void shutdown() {
		gameWindow.shutdown();
		lobby.shutdown();
	}

	public static void main(String[] args) {
		if(args.length != ARGS_COUNT) {
			logger.fatal(USAGE);
			return;
		}
		File resorucesFolder = new File(args[ARGS_INDEX_RESOURCES]);
		if(!resorucesFolder.exists() || !resorucesFolder.isDirectory()) {
			logger.fatal(NO_RESOURCES + "(" + resorucesFolder.getAbsolutePath() + ")");
		}
		Config.setResourcePath(resorucesFolder.getPath());
		Config config = null;
		try {
			config = new ConfigReader().readConfig(args[ARGS_INDEX_CONFIG], Config.getMultiplayerFieldConfig());
			config.validate();
		}
		catch(ConfigReaderException e) {
			logger.fatal(e);
			return;
		}
		catch(ConfigValidationException e) {
			logger.fatal(e);
			return;
		}

		BlazingBubble client = new BlazingBubble();

		try {
			client.startup(config);
			client.execute();
		}
		catch(StartupException e) {
			logger.fatal(e);
		}
		finally {
			client.shutdown();
		}
	}

	@Override
	public void gameSelected(int gamePort) {
		try {
			NetGameCommandFactory netGameCommandFactory = (NetGameCommandFactory)gameCommandFactory;

			GameStrategyFactory gameStrategyFactory = new GameStrategyFactory(
					gameWindow, gameWindow.getGamePanel().getGameStartup(), game, netGameCommandFactory);
			gameStrategyFactory.getGameStartedStrategy().addStartGameListener(gameWindow);
			gameStrategyFactory.getGameStartedStrategy().addStartGameListener(this);
			netGame.startup(config.getServerAddress(), gamePort, gameStrategyFactory);

			netGameCommandFactory.startup(netGame.getSender(), netGame.getServerConnection(), netGame.getQueryPort());

			netGame.setPreLoginResponseListener(netGameCommandFactory.getJoinGame());
		}
		catch(StartupException e) {
			//TODO : make some listener for this that displays the error message
			logger.error(e);
		}
	}

	@Override
	public void hostPromoted(long hostIdentification) {
		((NetGameCommandFactory)gameCommandFactory).startupHostActions(hostIdentification);
	}

	@Override
	public void gameStarted() {
		game.setActive(true);
	}

	@Override
	public void createLocalGame(Settings settings) {
		// TODO Auto-generated method stub

	}

	@Override
	public void joinGameDirectByIP(InetSocketAddress serverAddress) {
		// TODO Auto-generated method stub
		
	}
}
